// test/main.go 改进版本
package main

import (
	"context"
	"fmt"
	"log"
	"math/rand"
	"net/http"
	"sync"
	"time"
)

const (
	baseURL      = "http://localhost:8080/user"
	targetRPS    = 1000             // 目标每秒请求数
	rampUp       = 20 * time.Second // 加压时间
	testDuration = 30 * time.Second
	timeout      = 8 * time.Second // 单个请求超时时间
)

func main() {
	client := &http.Client{
		Timeout: timeout,
		Transport: &http.Transport{
			MaxIdleConns:        200,
			MaxIdleConnsPerHost: 200,
			IdleConnTimeout:     90 * time.Second,
		},
	}

	var (
		success     int64
		failed      int64
		totalTime   time.Duration
		mu          sync.Mutex
		wg          sync.WaitGroup
		ctx, cancel = context.WithTimeout(context.Background(), testDuration)
	)
	defer cancel()

	// 启动统计协程
	ticker := time.NewTicker(1 * time.Second)
	defer ticker.Stop()
	go func() {
		for range ticker.C {
			mu.Lock()
			log.Printf("Current RPS: %d (success) / %d (failed)",
				success, failed)
			success = 0
			failed = 0
			mu.Unlock()
		}
	}()

	// 压力控制器
	start := time.Now()
	rate := float64(targetRPS) / rampUp.Seconds()

	// Worker协程
	for i := 0; i < 200; i++ { // 根据CPU核心数调整
		wg.Add(1)
		go func() {
			defer wg.Done()
			for ctx.Err() == nil {
				// 动态计算请求间隔
				elapsed := time.Since(start).Seconds()
				currentRPS := rate * elapsed
				if currentRPS > float64(targetRPS) {
					currentRPS = float64(targetRPS)
				}
				delay := time.Duration(float64(time.Second)/currentRPS) * time.Nanosecond

				select {
				case <-ctx.Done():
					return
				case <-time.After(delay):
					userID := rand.Intn(1000) + 1
					sendRequest(client, userID, &mu, &success, &failed, &totalTime)
				}
			}
		}()
	}

	// 等待测试结束
	<-ctx.Done()
	wg.Wait()

	// 输出最终报告
	fmt.Printf("\n=== 最终测试报告 ===\n")
	fmt.Printf("测试时长:\t\t%s\n", testDuration)
	fmt.Printf("总请求数:\t\t%d\n", success+failed)
	fmt.Printf("成功率:\t\t%.2f%%\n", float64(success)/float64(success+failed)*100)
	fmt.Printf("平均延迟:\t\t%v\n", totalTime/time.Duration(success))
	fmt.Printf("QPS(每秒查询数):\t%.1f\n", float64(success)/testDuration.Seconds())
}

func sendRequest(client *http.Client, userID int, mu *sync.Mutex, success, failed *int64, totalTime *time.Duration) {
	start := time.Now()
	url := fmt.Sprintf("%s/%d", baseURL, userID)

	resp, err := client.Get(url)
	latency := time.Since(start)

	mu.Lock()
	defer mu.Unlock()

	if err == nil && resp != nil && resp.StatusCode == 200 {
		*success++
		*totalTime += latency
	} else {
		*failed++
		if resp != nil {
			log.Printf("ERROR [%d] %s", resp.StatusCode, url)
		}
	}
	if resp != nil {
		err := resp.Body.Close()
		if err != nil {
			return
		}
	}
}
